#include "const/oPreDefine.h"
#include "platform/oUnit.h"
#include "platform/oAction.h"
#include "platform/oData.h"
#include "platform/oBulletDef.h"
#include "platform/oAINode.h"
#include "platform/oAI.h"
using namespace platform;
#include "model/oModelDef.h"
#include "model/oModel.h"
#include "physics/oSensor.h"
#include "physics/oGroup.h"

oUnit::oUnit(oWorld* world, oUnitDef* unitDef):
oBody(world, unitDef->getBodyDef()),
_model(nullptr),
_detectSensor(nullptr),
_attackSensor(nullptr),
_isFaceRight(true),
_currentAction(nullptr),
_size(unitDef->getSize()),
move(unitDef->move),
moveSpeed(1.0f),
jump(unitDef->jump),
attackSpeed(1.0f),
hp(this, unitDef->maxHp),
maxHp(unitDef->maxHp),
attackBase(unitDef->attackBase),
attackBonus(0),
attackFactor(1.0f),
attackType(unitDef->attackType),
attackTarget(unitDef->attackTarget),
attackPower(unitDef->attackPower),
targetAllow(unitDef->targetAllow),
damageType(unitDef->damageType),
defenceType(unitDef->defenceType),
sensity(unitDef->sensity),
_bulletDef(unitDef->getBulletDef()),
_unitDef(unitDef),
_reflexArc(nullptr)
{
	properties.setOwner(this);
	instincts.setOwner(this);
	oUnit::setDetectDistance(unitDef->detectDistance);
	oUnit::setAttackRange(unitDef->attackRange);
	CC_SAFE_RETAIN(_bulletDef);
	_unitDef->retain();
	_groundSensor = oBody::getSensorByTag(oUnitDef::GroundSensorTag);
	this->scheduleUpdate();
}

oUnit::~oUnit()
{
	BOOST_FOREACH(const oActionMap::value_type& it, _actions)
	{
		delete it.second;
	}
	_actions.clear();
	CC_SAFE_RELEASE_NULL(_model);
	CC_SAFE_RELEASE_NULL(_bulletDef);
	CC_SAFE_RELEASE_NULL(_reflexArc);
	CC_SAFE_RELEASE_NULL(_unitDef);
}

oUnitDef* oUnit::getUnitDef() const
{
	return _unitDef;
}

void oUnit::setFaceRight( bool var )
{
	if (_isFaceRight != var)
	{
		_isFaceRight = var;
		if (_model)
		{
			//_model->runAction(var == _model->getModelDef()->isFaceRight() ? CCScaleTo::actionWithDuration(0.1f, 1.0f, 1.0f) : CCScaleTo::actionWithDuration(0.1f, -1.0f, 1.0f));
			this->setScaleX(var == _model->getModelDef()->isFaceRight() ? 1.0f : -1.0f);
		}
	}
}

bool oUnit::isFaceRight() const
{
	return _isFaceRight;
}

void oUnit::setModel( oModel* model )
{
	if (_model != model)
	{
		if (_model != nullptr)
		{
			_model->release();
			this->removeChild(_model, true);
		}
		if (model)
		{
			model->retain();
			this->addChild(model);
		}
		_model = model;
		this->setScaleX(_isFaceRight == _model->getModelDef()->isFaceRight() ? 1.0f : -1.0f);
	}
}

oModel* oUnit::getModel() const
{
	return _model;
}

oUnit* oUnit::node(oWorld* world, oUnitDef* unitDef)
{
	oUnit* unit = new oUnit(world, unitDef);
	unit->autorelease();
	return unit;
}

void oUnit::update( ccTime dt )
{
	if (_currentAction != nullptr)
	{
		_currentAction->update(dt);
		if (_currentAction && !_currentAction->isDoing())
		{
			_currentAction = nullptr;
		}
	}
	else
	{
		oAI::conditionedReflex(this);
	}
}

void oUnit::setGroup( oGroup* group )
{
	_group = group;
	for (b2Fixture* f = _bodyB2->GetFixtureList();f;f = f->GetNext())
	{
		if (f->IsSensor())
		{
			oSensor* sensor = (oSensor*)f->GetUserData();
			if (sensor->getTag() == oUnitDef::DetectSensorTag || sensor->getTag() == oUnitDef::AttackSensorTag)
			{
				f->SetFilterData(oData::sharedData().getSensePlayerGroup());
			}
			else
			{
				f->SetFilterData(group);
			}
		}
		else
		{
			f->SetFilterData(group);
		}
	}
}

float oUnit::getWidth() const
{
	return _size.width;
}

float oUnit::getHeight() const
{
	return _size.height;
}

oAction* oUnit::attachAction( int id )
{
	oActionMap::iterator it = _actions.find(id);
	if (it == _actions.end())
	{
		oAction* action = oAction::create(id, this);
		if (action)
		{
			_actions[id] = action;
		}
		return action;
	}
	return it->second;
}

void oUnit::attachAction( oAction* action )
{
	oActionMap::iterator it = _actions.find(action->getId());
	if (it != _actions.end())
	{
		delete it->second;
		_actions.erase(it);
	}
	_actions[action->getId()] = action;
}

oAction* oUnit::getAction( int id ) const
{
	oActionMap::const_iterator it = _actions.find(id);
	return it == _actions.end() ? nullptr : it->second;
}

bool oUnit::doIt( int id )
{
	oActionMap::iterator it = _actions.find(id);
	if (it != _actions.end())
	{
		oAction* action = it->second;
		if (!action->isDoing() && action->isAvailable())
		{
			if (_currentAction != nullptr && _currentAction->isDoing())
			{
				if (_currentAction->getPriority() < action->getPriority())
				{
					_currentAction->stop();
					_currentAction = nullptr;
				}
				else
				{
					return false;
				}
			}
			action->run();
			if (action->isDoing())
			{
				_currentAction = action;
			}
			return true;
		}
	}
	return false;
}

bool oUnit::isOnSurface() const
{
	return _groundSensor && !_groundSensor->getSensedBodies().empty();
}

void oUnit::setDetectDistance(float var)
{
	_detectDistance = var;
	if (_detectSensor)
	{
		oBody::removeSensor(_detectSensor);
		_detectSensor = nullptr;
	}
	if (var > 0)
	{
		b2Fixture* fixture = oBody::attach(oBodyDef::circleSensor(oUnitDef::DetectSensorTag, var));
		fixture->SetFilterData(oData::sharedData().getSensePlayerGroup());
		_detectSensor = (oSensor*)fixture->GetUserData();
	}
}

void oUnit::setAttackRange(const CCSize& var)
{
	_attackRange = var;
	if (_attackSensor)
	{
		oBody::removeSensor(_attackSensor);
		_attackSensor = nullptr;
	}
	if (var.width != 0.0f && var.height != 0.0f)
	{
		b2Fixture* fixture = oBody::attach(oBodyDef::polygonSensor(oUnitDef::AttackSensorTag, var.width*2, var.height));
		fixture->SetFilterData(oData::sharedData().getSensePlayerGroup());
		_attackSensor = (oSensor*)fixture->GetUserData();
	}
}

const CCSize& oUnit::getAttackRange() const
{
	return _attackRange;
}

float oUnit::getDetectDistance() const
{
	return _detectDistance;
}

oSensor* oUnit::getGroundSensor() const
{
	return _groundSensor;
}

oSensor* oUnit::getDetectSensor() const
{
	return _detectSensor;
}

oSensor* oUnit::getAttackSensor() const
{
	return _attackSensor;
}

void oUnit::setBulletDef(oBulletDef* var)
{
	CC_SAFE_RELEASE(_bulletDef);
	_bulletDef = var;
	_bulletDef->retain();
}

oBulletDef* oUnit::getBulletDef() const
{
	return _bulletDef;
}

oAction* oUnit::getCurrentAction() const
{
	return _currentAction;
}

void oUnit::oPropertySet::setOwner( oUnit* owner )
{
	_owner = owner;
	oProperty* prop = &_owner->hp;
	prop->retain();
	_items["hp"] = prop;//Add the default property "hp"
}

oProperty& oUnit::oPropertySet::operator[]( const string& name )
{
	auto it = _items.find(name);
	if (it != _items.end())
	{
		return *it->second;
	}
	else
	{
		oProperty* prop = new oProperty(_owner, 0);
		_items[name] = prop;
		_owner->instincts.reinstall();
		return *prop;
	}
}

const oProperty& oUnit::oPropertySet::operator[]( const string& name ) const
{
	return *_items[name];
}

void oUnit::oPropertySet::remove( const string& name )
{
	auto it = _items.find(name);
	if (it != _items.end() && it->second != &_owner->hp)
	{
		it->second->release();
		_items.erase(it);
	}
}

void oUnit::oPropertySet::clear()
{
	BOOST_FOREACH(const oPropertyMap::value_type& item, _items)
	{
		item.second->release();
	}
	_items.clear();
	oProperty* prop = &_owner->hp;
	prop->retain();
	_items["hp"] = prop;//Add the default property "hp"
}

oProperty* oUnit::oPropertySet::get( const string& name ) const
{
	auto it = _items.find(name);
	return it != _items.end() ? it->second : nullptr;
}

oUnit::oPropertySet::~oPropertySet()
{
	oPropertySet::clear();
}

void oUnit::oInstinctSet::add( oInstinct* instinct )
{
	_instincts.push_back(instinct);
	instinct->install(_owner);
	instinct->retain();
}

void oUnit::oInstinctSet::remove( oInstinct* instinct )
{
	for (auto it = _instincts.begin();it != _instincts.end();it++)
	{
		if (*it == instinct)
		{
			instinct->uninstall(_owner);
			instinct->release();
			_instincts.erase(it);
			break;
		}
	}
}

void oUnit::oInstinctSet::clear()
{
	BOOST_FOREACH(oInstinct* instinct, _instincts)
	{
		instinct->uninstall(_owner);
		instinct->release();
	}
	_instincts.clear();
}

void oUnit::oInstinctSet::setOwner( oUnit* owner )
{
	_owner = owner;
}

void oUnit::oInstinctSet::reinstall()
{
	BOOST_FOREACH(oInstinct* instinct, _instincts)
	{
		instinct->install(_owner);
	}
}

void oUnit::setReflexArc(oAILeaf* var)
{
	if (_reflexArc)
	{
		_reflexArc->release();
	}
	_reflexArc = var;
	_reflexArc->retain();
	oAI::conditionedReflex(this);
}

oAILeaf* oUnit::getReflexArc() const
{
	return _reflexArc;
}
